【题目描述】
给定一个有n个顶点m条边的加权有向图,如果图中存在环(回路),环的平均值等于,环上边的权值之和除以构成环的边数,图中可能不止存在一个回路,计算平均权值最小的回路。【Sample Input】
(输入第一行为数据组数T(t<=10),每组数据第一行为图的点数n和边数m(n<=50).以下m行3个正整数u,v,w,表示有一条从u到v的有向边,权值为w,输入没有自环。)
2
2 1
1 2 1
2 2
1 2 2
2 1 3【Sample Output】
(对于每组数据输出最小平均值,保留两位小数。如果无解,输出“No cycle found.” 每组数据输出格式如下(x表示第几组数据): Case #x:最小平均值或 No cycle found.)
Case #1: No cycle found.
Case #2: 2.50【数据范围】
T<=10;n ≤ 50,m<=n*n;u, v ≤ n;w ≤ 10000000
【题解】二分+spfa或bellman ford判负环
本题要求最小平均环。直接搜索显然会超时(大概搜索拿了30)。在很难直接确定答案的情况下,考虑用二分来做。
二分之后我们有了可能的答案mid。考虑如果答案可行,即某个环的平均值小于等于此可能解,设这个环上的边劝为w[1]至w[k],则可以得到:
w[1]+w[2]+…+w[k]<=k*mid
→ (w[1]-mid)+(w[2]-mid)+…+(w[k]-mid)<=0
故而我们将所有边的权值减去mid,判断图内是否存在负环,若有则最小平均值<=当前答案。如果无论二分值多大都无法得到负环,则证明此图无环。
#include <cstdio>
#include <iostream>
#include <cstring>
#define N 55
#define inf 1000000000
int n,m,T,u[N*N],v[N*N],w[N*N];
double dis[N],ans;
bool work(double x)
{
for (int i=1;i<=n;++i) dis[i]=0;
bool bo;
for (int i=1;i<=n;++i)
{
bo=false;
for (int j=1;j<=m;++j)
if (dis[v[j]]>dis[u[j]]+w[j]-x) dis[v[j]]=dis[u[j]]+w[j]-x,bo=true;
if (!bo) return false;
}
return true;
}
int main()
{
int cas=1;
for (scanf("%d\n",&T);T--;++cas)
{
printf("Case #%d: ",cas);
scanf("%d%d\n",&n,&m);
for (int i=1;i<=m;++i)
scanf("%d%d%d\n",&u[i],&v[i],&w[i]);
double l=0,r=1e8;
ans=-1.0;
for (;l+1e-6<r;)
{
double mid=0.5*(l+r);
if (work(mid)) r=mid,ans=mid;
else l=mid;
}
if (ans<0) printf("No cycle found.\n");
else printf("%.2f\n",ans);
}
return 0;
}